#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/un.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <assert.h>

#include <poller.h>
#include <decode_unit.h>
#include <listener.h>
#include <memcheck.h>
#include <http_decoder.h>
#include <log.h>
#include <net.h>
#include <client_unit.h>
#include <helper_unit.h>
#include <global.h>
#include "dll.h"


extern map<int,__http_svr_ns__::FdInfo>* g_pFdInfoMap;
extern __http_svr_ns__::CDecodeUnitSet *g_pDecodeUnitSet;

HTTP_SVR_NS_BEGIN

CListener::CListener (const char* bindIp, uint16_t port, int acceptcnt) : 
CPollerObject (NULL, -1),
_pollerunit (NULL),
_decoder (NULL),
_accept_cnt (acceptcnt),
_newfd_cnt (0),
_fd_array (NULL)
{
    strncpy (_bindAddr, bindIp, sizeof(_bindAddr) - 1);

    _bindPort = port;

    //calloc fd array
    _fd_array = (int*) CALLOC (acceptcnt, sizeof(int));
    assert (NULL != _fd_array);


    memset (_fd_array, -1, acceptcnt * sizeof(int));
    _decoder = CHttpDecoder::Instance ();
}

CListener::~CListener()
{
    //free fd array
    FREE_CLEAR(_fd_array);

    CHttpDecoder::Destroy();
}

CDecoderUnit* CListener::create_decoderunit (THttpStateInfo* httpinfo)
{
    CDecoderUnit* decoderunit = NULL;
    
    //create decoder unit
    NEW (CDecoderUnit(this, httpinfo), decoderunit);

    if (NULL == decoderunit)
    {
        log_error ("create CDecoderUnit object failed.");

        return NULL;
    }

    decoderunit->pollerunit (_pollerunit);

    return decoderunit;
}

int CListener::Attach (CPollerUnit* unit,
                       int blog)
{
	if ((netfd = CNet::tcp_bind(_bindAddr, _bindPort, blog)) == -1)
	{
        log_error ("bind addr[%s], port[%d] failed.", _bindAddr, _bindPort);
        return -1;
	}
	
	_pollerunit      = unit;

	EnableInput ();
    
	return AttachPoller(unit);
}

int CListener::proc_request (struct sockaddr_in* peer)
{
    CDecoderUnit*       pDecoderunit    = NULL;
    THttpStateInfo*     pHttpStateInfo  = NULL;

    for (int i = 0; i < _newfd_cnt; ++i)
    {
        if (_fd_array[i] == -1)
        {
            continue;
        }

        bool bCheckQvia = false;

        NEW (THttpStateInfo, pHttpStateInfo);

        if (NULL == pHttpStateInfo)
        {
            log_error ("create THttpStatInfo object failed, address[%s:%d] fd[%d], msg[%m]", _bindAddr, _bindPort, netfd);
            ::close (_fd_array[i]);
            _fd_array[i] = -1;

            continue;
        }

        if (NULL == pHttpStateInfo->_allsendbuf)
        {
            log_error ("create allsendbuf head failed, address[%s:%d] fd[%d], msg[%m]", _bindAddr, _bindPort, netfd);

            ::close (_fd_array[i]);
            _fd_array[i] = -1;

            continue;
        }

        //set fd
        pHttpStateInfo->_fd = _fd_array[i];
        //set ip
        pHttpStateInfo->_ip = peer->sin_addr.s_addr;
		pHttpStateInfo->_client_ip = inet_ntoa(peer->sin_addr);
        //create CDecoderUnit object
        pDecoderunit = create_decoderunit (pHttpStateInfo);

        if (NULL == pDecoderunit)
        {
            DELETE (pHttpStateInfo);
            log_error ("create CDecoderUnit object failed, address[%s:%d], fd[%d]", _bindAddr, _bindPort, netfd);

            ::close (_fd_array[i]);
            _fd_array[i] = -1;

            continue;
        }

        //set the flag to determin whether check qvia or not
        pDecoderunit->set_qvia_flag(bCheckQvia);

        if(pDecoderunit->listener_input() < 0)
        {
            ::close(_fd_array[i]);
            _fd_array[i] = -1;

            DELETE (pDecoderunit);
            pDecoderunit = NULL;

            continue;
        }

		log_debug("*STEP: accept new tcp connection, address[%s:%d], fd[%d]", _bindAddr, _bindPort, pHttpStateInfo->_fd);
        
        FdInfo &fdInfo = (*g_pFdInfoMap)[pHttpStateInfo->_fd];
        ConnectInfo &cInfo = fdInfo.connectInfo;
        cInfo.fd        = pHttpStateInfo->_fd;
        cInfo.clientIP  = pHttpStateInfo->_client_ip;
        cInfo.ptr       = NULL;
        cInfo.proxyFd   = 0;

        dll.handle_open(&cInfo);
    }

    return 0;
}

int CListener::proc_accept (struct sockaddr_in* peer, socklen_t* peerSize)
{
    int newfd = -1;

    memset (_fd_array, -1, _accept_cnt * sizeof(int));
    _newfd_cnt  = 0;

    for (int i = 0; i < _accept_cnt; ++i)
    {
		newfd = ::accept (netfd, (struct sockaddr*)peer, peerSize);

		if (newfd < 0)
		{
			if (errno == EINTR)
            {
                continue;
            }

            if (errno == EAGAIN)
            {
                return -1;
            }

			log_error ("*STEP: accept new connection failed, address[%s:%d], fd[%d], msg[%m]", _bindAddr, _bindPort, netfd);
        }
		
        _fd_array[i] = newfd;
        _newfd_cnt++;
    }

    return 0;
}

int CListener::InputNotify (void)
{
    struct sockaddr_in  peer;
    socklen_t           peerSize;
    int                 ret;

    peerSize = sizeof (struct sockaddr_in);
 
	while (true)
	{
        ret = proc_accept (&peer, &peerSize);

        if (_newfd_cnt <= 0)
        {
            log_error ("invoke proc_accept failed, new fd count = %d", _newfd_cnt);

            return POLLER_SUCC; 
        }

        if (_newfd_cnt > 0)
        {
            //add web all request
            proc_request (&peer);
        }

        if (ret < 0)
        {
            return POLLER_SUCC;
        }
	}
}

void CListener::complete (CDecoderUnit* decoderunit)
{
    int webfd      = 0;
    int helperfd   = 0;

    if (NULL != decoderunit->_webUnit)
    {
        webfd = decoderunit->_webUnit->get_netfd ();
    }

    if (NULL != decoderunit->_helperUnit)
    {
        helperfd = decoderunit->_helperUnit->get_netfd ();
    }

    log_info("*STEP: handle HTTP request completed, listenfd[%d], webfd[%d], helperfd[%d]", netfd, webfd, helperfd);
    
    //DELETE (decoderunit);
    if(g_pDecodeUnitSet->find(decoderunit) != g_pDecodeUnitSet->end()){
        log_warning("Warning: decode_unit:%p double deleted,fd:%d",decoderunit, webfd )   ;
	}
    g_pDecodeUnitSet->insert(decoderunit);
}

HTTP_SVR_NS_END

